import axios, { AxiosResponse } from "axios";
import { RespResultCode, config } from "./config";
import { jsonToParams, paramHandler } from "@/utils/common.methods";
import { Message } from "@arco-design/web-vue";
import { appContext } from "@/models/app.context";

export interface StatusInfo {
  message?: string;
  msg?: string;
  code: number;
}

export type BaseRespInfo = {
  status: StatusInfo;
  data: any;
};

export class BaseServices {
  constructor() { }
  private controller = new Map<string, AbortController>();

  private header(ContentType: string = "application/json"): any {
    const token = appContext.global.token;
    return paramHandler({
      "Access-Control-Allow-Origin": "*",
      "Content-Type": ContentType,
      token,
    });
  }

  /**
   * 网络请求统一处理
   * @param url 地址
   * @param data 参数
   * @param ContentType 格式
   * @param responseType  ResponseType 返回格式
   * @returns
   */
  public request<T>(uri: string, data: Object, method: "POST" | "OPTIONS" | "GET" | "HEAD" | "PUT" | "DELETE" | "TRACE" | "CONNECT" = "POST", headers?: any): Promise<T> {
    if (!headers) headers = this.header();
    data = paramHandler(data)
    let url = uri?.includes("http") ? uri : config.httpHost + uri;
    url = method === "GET" ? url + "?" + jsonToParams(data) : url;
    console.log("%c url：" + url, "background: #41b883;color: #ffffff;border-radius: 0px;border: 2px solid #41b883;padding: 0 2px");
    console.log("request >>>", data);
    return new Promise<T>((resolve, reject) => {
      axios({ url, method, data, headers })
        .then((response: AxiosResponse) => {
          console.log("response >>>", response);
          const flag = this.checkResp(response);
          console.log(flag);
          if (flag) {
            resolve(response.data as T);
          } else {
            reject(response);
          }
        })
        .catch((err) => {
          console.error("request error", err);
          reject(err);
        });
    });
  }
  public get<T>(url: string): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      axios
        .get(url)
        .then((res: any) => {
          if (this.checkResp(res)) {
            resolve(res.data as T);
          }
        })
        .catch((err) => {
          console.log(err);
          reject(err);
        });
    });
  }

  /**
   * 流数据请求
   * @param key this.controller AbortController - key, 用于中断请求
   * @param url
   * @param data
   * @param onDataReceived 流数据接收中
   * @param end            流数据结束时
   * @param method
   */
  public stream(key: string, url: string, data: Object, onDataReceived: (data: string) => void, end?: (data: string) => void, error?: (data: string) => void, method: "POST" | "OPTIONS" | "GET" | "HEAD" | "PUT" | "DELETE" | "TRACE" | "CONNECT" = "POST"): void {
    let res = "";
    const controller = new AbortController();
    this.controller.set(key, controller);
    const signal = controller.signal;
    fetch(url, { method, headers: this.header(), body: JSON.stringify({ ...data }), signal })
      .then(function (response) {
        var reader = response.body.getReader();
        function read() {
          return reader.read().then(function (result) {
            if (result.done) {
              // 流数据结束时
              if (end) end(res);
              return;
            }
            // 创建一个 TextDecoder 对象，并指定要使用的字符编码为 UTF-8
            var decoder = new TextDecoder("utf-8");
            // 使用 TextDecoder 对象将 Uint8Array 数据解码为字符串
            const chunk = decoder.decode(result.value);
            res += chunk;
            onDataReceived(chunk);
            // 继续读取下一个块
            return read();
          });
        }
        return read();
      })
      .catch(function (err) {
        if (err.name === "AbortError") {
          // 请求被中止
          console.error("请求被中止");
          if (end) {
            end(res);
            error("--- 请求被中止 ---");
          }
        } else {
          if (end) {
            error("啊！出错了，请稍后再来试试吧");
            end('')
          }
        }
      });
  }

  private checkResp(res: AxiosResponse<BaseRespInfo>): boolean {
    if (res.status === 200) {
      // 因源服务器无法统一，故接口逻辑应该独立判断
      const { status } = res.data;
      // if (data.token) AppContext.instance.setGlobal({ token: data.token });  // 更新token
      if (status?.code) {
        console.log(status?.code);
        Message.error(status.message || status.msg);
        if ([RespResultCode.TokenEmpty, RespResultCode.TokenExpire, RespResultCode.TokenMiss, RespResultCode.DataCorrupted].includes(status?.code)) {
          console.log("re");
          localStorage.removeItem("token");
        }
        return false;
      }
      return true;
    }
    return false;
  }

  /** stop fetch */
  public stopStream(key?: string): void {
    // TODO
    if (this.controller?.size) {
      if (key) {
        const controller = this.controller.get(key);
        controller.abort();
      } else {
        console.log(this.controller);
        this.controller.forEach((val) => val.abort());
      }
    }
  }
}
